package com.ptrans.server;

import static com.ptrans.utilities.CommonUtilities.IMG_PATH;
import static com.ptrans.utilities.CommonUtilities.SERVER_URL;
import static com.ptrans.utilities.CommonUtilities.TAG;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.w3c.dom.Node;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import com.ptrans.utilities.ParserXML;

/**
 * Class allowing to have a connection with a specific server.
 * @author Jean
 *
 */
public class ServerHttp {
	
	private static final int MAX_ATTEMPTS = 5;
    private static final int BACKOFF_MILLI_SECONDS = 2000;
    private static final Random random = new Random();
	
    /**
     * Returned the URL with params : http://toto.com/?e=x&z=y for example
     * @param s
     * @param params
     * @return
     */
	public static String makeGetUrl(String s, Map<String, String> params) {
		
		StringBuilder bodyBuilder = new StringBuilder();
		bodyBuilder.append(s);
		if(params.size() != 0) bodyBuilder.append("?");
        Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
        // constructs the Get body using the parameters
        while (iterator.hasNext()) {
            Entry<String, String> param = iterator.next();
            bodyBuilder.append(param.getKey()).append('=')
                    .append(param.getValue());
            if (iterator.hasNext()) {
                bodyBuilder.append('&');
            }
        }
		
		return bodyBuilder.toString();
	}
	
	/**
	 * Send an http request by GET method to a specific endPoint, with specific params.
	 * @param endPoint
	 * @param params
	 * @return Node object
	 * @throws IOException
	 */
	public static Node sendGet(String endPoint, Map<String, String> params) throws IOException {
		URL url;
		
		String urlString = makeGetUrl(endPoint,params);
		
		try {
            url = new URL(urlString);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + endPoint);
        }
		
		Log.v(TAG, urlString);
		
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		// optional default is GET
		con.setRequestMethod("GET");
 
		int responseCode = con.getResponseCode();
		Log.v(TAG, "\nSending 'GET' request to URL : " + url);
		Log.v(TAG, "Response Code : " + responseCode);
 
		BufferedReader in = new BufferedReader(
		        new InputStreamReader(con.getInputStream()));
		String inputLine;
		StringBuffer response = new StringBuffer();
 
		while ((inputLine = in.readLine()) != null) {
			response.append(inputLine);
		}
		in.close();
		Log.v(TAG, "Response String : " + response.toString());
		
		return ParserXML.convertStringToNode(response.toString());
	}
	
	/**
	 * Try a connection to a specific server, with multiple tries. (5)
	 * @param serverUrl
	 * @param params
	 * @param context
	 * @return
	 */
	public static Node tryAConnection(String serverUrl, Map<String, String> params, Context context) {
		Node response = null;
		
		long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
		
		for (int i = 1; i <= MAX_ATTEMPTS; i++) {
            Log.d(TAG, "Attempt #" + i + " to register");
            try {
                Log.i(TAG, "server registering on " + i + " attempt");
                response = ServerHttp.sendGet(serverUrl, params);
                return response;
            } catch (IOException e) {
                // Here we are simplifying and retrying on any error; in a real
                // application, it should retry only on unrecoverable errors
                // (like HTTP error code 503).
                Log.e(TAG, "Failed to register on attempt " + i + ":" + e);
                if (i == MAX_ATTEMPTS) {
                    break;
                }
                try {
                    Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
                    Thread.sleep(backoff);
                } catch (InterruptedException e1) {
                    // Activity finished before we complete - exit.
                    Log.d(TAG, "Thread interrupted: abort remaining retries!");
                    Thread.currentThread().interrupt();
                    return response;
                }
                // increase backoff exponentially
                backoff *= 2;
            }
        }
		return response;
	}
	
	/**
	 * upload picture by POST method
	 * @param src : Source of picture on SDCard
	 * @param name : Name of picture on server
	 * @return
	 */
	public static Node uploadPicture(String src, String name) {
		String upLoadServerUri = SERVER_URL;
		int serverResponseCode = 0;
		
        HttpURLConnection conn = null;
        DataOutputStream dos = null;  
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024 * 1024; 
        File sourceFile = new File(src); 
        
        if (!sourceFile.isFile()) {
        	Log.e("uploadFile", "Source File Does not exist");
        	return null;
        }
        try {
        	FileInputStream fileInputStream = new FileInputStream(sourceFile);
            URL url = new URL(upLoadServerUri);
            conn = (HttpURLConnection) url.openConnection(); // Open a HTTP  connection to  the URL
            conn.setDoInput(true); // Allow Inputs
            conn.setDoOutput(true); // Allow Outputs
            conn.setUseCaches(false); // Don't use a Cached Copy
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("ENCTYPE", "multipart/form-data");
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
            conn.setRequestProperty("uploaded_file", src); 
            dos = new DataOutputStream(conn.getOutputStream());
  
            dos.writeBytes(twoHyphens + boundary + lineEnd); 
            dos.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\";filename=\""+ name + ".jpg\"" + lineEnd);
            dos.writeBytes(lineEnd);
  
            bytesAvailable = fileInputStream.available(); // create a buffer of  maximum size
  
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            buffer = new byte[bufferSize];
  
            // read file and write it into form...
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);  
              
            while (bytesRead > 0) {
              dos.write(buffer, 0, bufferSize);
              bytesAvailable = fileInputStream.available();
              bufferSize = Math.min(bytesAvailable, maxBufferSize);
              bytesRead = fileInputStream.read(buffer, 0, bufferSize);               
             }
  
            // send multipart form data necesssary after file data...
            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
  
            // Responses from the server (code and message)
            serverResponseCode = conn.getResponseCode();
            
            BufferedReader in = new BufferedReader(
    		        new InputStreamReader(conn.getInputStream()));
    		String inputLine;
    		StringBuffer response = new StringBuffer();
     
    		while ((inputLine = in.readLine()) != null) {
    			response.append(inputLine);
    		}
            
            Log.i("uploadFile", "HTTP Response is : " + response.toString() + ": " + serverResponseCode);
            if(serverResponseCode == 200){
            	Log.i("uploadIMG", "File Upload Complete."); 
            	return ParserXML.convertStringToNode(response.toString()); 
            	
            } else {
            	Log.i("uploadIMG", "An error was occured during the upload"); 
            }
            
            //close the streams //
            fileInputStream.close();
            dos.flush();
            dos.close();
             
       } catch (MalformedURLException ex) {  
           Log.e("Upload file to server", "error: " + ex.getMessage(), ex);  
       } catch (Exception e) {
           Log.e("Upload file to server Exception", "Exception : " + e.getMessage(), e);  
       }
        
       return null;
        
	}
	
}
